import 'dart:math';

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';

import '../model/entity.dart';
import './orntdrag.dart';

class CardStackWidget extends StatefulWidget {
  final List<CardEntity> cardList;

  final int cardCount;
  final double offset;
  final EdgeInsetsGeometry cardPadding;

  const CardStackWidget({
    Key? key,
    required this.cardList,
    this.cardCount = 2,
    this.offset = 10,
    this.cardPadding = const EdgeInsets.only(left: 20, right: 20, top: 60),
  }) : super(key: key);

  @override
  _CardStackWidgetState createState() => _CardStackWidgetState();
}

class _CardStackWidgetState extends State<CardStackWidget>
    with TickerProviderStateMixin {
  double _ratio = 0;
  double _totalDx = 0;
  double _totalDy = 0;
  late Map<Type, GestureRecognizerFactory> _cardGestures;
  AnimationController? _animationController;
  bool _isDragging = false;

  @override
  void initState() {
    _totalDx = _totalDy = 0;
    _cardGestures = {
      DirectionGestureRecognizer:
          GestureRecognizerFactoryWithHandlers<DirectionGestureRecognizer>(
        () => DirectionGestureRecognizer(DirectionGestureRecognizer.left |
            DirectionGestureRecognizer.right |
            DirectionGestureRecognizer.up),
        (instance) {
          instance.onDown = _onPanDown;
          instance.onUpdate = _onPanUpdate;
          instance.onEnd = _onPanEnd;
        },
      ),
      TapGestureRecognizer:
          GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
        () => TapGestureRecognizer(),
        (instance) {
          instance.onTap = _onCardTap;
        },
      )
    };
    super.initState();
  }

  _onCardTap() {
    if (widget.cardList.isNotEmpty) {
      Fluttertoast.showToast(msg: widget.cardList[0].text);
    }
  }

  _onPanDown(DragDownDetails details) {
    if (_isAnimating) {
      return;
    }
  }

  _onPanUpdate(DragUpdateDetails details) {
    if (_isAnimating) {
      return;
    }
    if (!_isDragging) {
      _isDragging = true;
      return;
    }
    _totalDx += details.delta.dx;
    _totalDy += details.delta.dy;

    if (context.size == null) {
      return;
    }
    Size contextSize = context.size!;

    _ratio =
        sqrt(_totalDx * _totalDx + _totalDy * _totalDy) / contextSize.width;
    _ratio = min(max(_ratio, 0), 1.0);
    setState(() {});
  }

  _onPanEnd(DragEndDetails details) {
    if (_isAnimating) {
      return;
    }
    _isDragging = false;

    if (context.size == null) {
      return;
    }
    Size contextSize = context.size!;

    if (_totalDx.abs() >= contextSize.width * 0.1 ||
        _totalDy.abs() >= contextSize.height * 0.1) {
      double endX, endY;
      if (_totalDx.abs() > _totalDy.abs()) {
        endX = contextSize.width * _totalDx.sign;
        endY =
            _totalDy.sign * contextSize.width * _totalDy.abs() / _totalDx.abs();
      } else {
        endY = contextSize.height * _totalDy.sign;
        endX = _totalDx.sign *
            contextSize.height *
            _totalDx.abs() /
            _totalDy.abs();
      }
      _startAnimator(Offset(_totalDx, _totalDy), Offset(endX, endY), true);
    } else {
      _startAnimator(Offset(_totalDx, _totalDy), Offset.zero, false);
    }
  }

  bool get _isAnimating =>
      _animationController != null && _animationController!.isAnimating;

  _startAnimator(Offset begin, Offset end, bool remove) {
    AnimationController animationController = AnimationController(
        duration: const Duration(milliseconds: 200), vsync: this);
    var animation = Tween(begin: begin, end: end).animate(
        CurvedAnimation(parent: animationController, curve: Curves.linear));
    animationController.addListener(() {
      setState(() {
        _totalDx = animation.value.dx;
        _totalDy = animation.value.dy;

        if (context.size == null) {
          return;
        }
        Size contextSize = context.size!;
        _ratio =
            sqrt(_totalDx * _totalDx + _totalDy * _totalDy) / contextSize.width;
        _ratio = min(max(_ratio, 0), 1.0);
      });
    });
    animationController.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        if (remove) {
          widget.cardList.removeAt(0);
        }
        _totalDx = 0;
        _totalDy = 0;
        _ratio = 0;
        setState(() {});
      }
    });
    animationController.forward();
    _animationController = animationController;
  }

  @override
  void dispose() {
    final animationController = _animationController;
    if (animationController != null) {
      animationController.dispose();
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (widget.cardList.isEmpty) {
      return Container();
    }

    List<Widget> children = [];
    int length = widget.cardList.length;
    int count = (length > widget.cardCount) ? widget.cardCount : length;
    for (int i = 0; i < count; i++) {
      double dx = i == 0 ? _totalDx : -_ratio * widget.offset;
      double dy = i == 0 ? _totalDy : _ratio * widget.offset;

      dx = _totalDx;
      dy = _totalDy;

      dx = i == 0 ? _totalDx : -1 * widget.offset;
      dy = i == 0 ? _totalDy : 1 * widget.offset;
      dy = i == 0 ? _totalDy : 1 * widget.offset;

      // if (i == 0) {
      //   debugPrint("$i: dx = $dx, dy=$dy, _ratio = $_ratio");
      // }
      Widget cardWidget = _CardWidget(
        cardEntity: widget.cardList[i],
        position: i,
        dx: dx,
        dy: dy,
        offset: widget.offset,
      );
      if (i == 0) {
        cardWidget = RawGestureDetector(
          gestures: _cardGestures,
          behavior: HitTestBehavior.deferToChild,
          child: cardWidget,
        );
      }
      children.add(Container(
        alignment: Alignment.topCenter,
        // color: Colors.red,
        padding: widget.cardPadding,
        child: cardWidget,
      ));
    }
    return Stack(
      children: children.reversed.toList(),
    );
  }
}

class _CardWidget extends StatelessWidget {
  final CardEntity cardEntity;
  final int position;
  final double dx;
  final double dy;
  final double offset;

  const _CardWidget({
    Key? key,
    required this.cardEntity,
    required this.position,
    this.dx = 0,
    this.dy = 0,
    required this.offset,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    double translationX = dx + (offset * position.toDouble());
    // double translationY = dy + (-offset * position.toDouble());
    double translationY = 0;
    double translationZ = 0.5;
    double rotateZAngle = pi / 30;

    double offsetXPercent = 0;
    double offsetXMax = 80;
    if (dx > offsetXMax) {
      offsetXPercent = 1;
    } else {
      offsetXPercent = dx / offsetXMax * 1.0;
    }
    rotateZAngle = offsetXPercent * pi / 30;
    translationY -= offsetXPercent * 30;

    if (position == 0) {
      debugPrint(
          "$position: dx = $dx, dy=$dy, offset = $offset, offsetXPercent=$offsetXPercent,rotateZAngle= $rotateZAngle");
    }

    Matrix4 transform = Matrix4.identity();
    transform.translate(translationX, translationY, translationZ);
    transform.rotateZ(rotateZAngle);

    return AspectRatio(
      aspectRatio: 372 / 673.0,
      child: Transform(
        transform: transform,
        child: ClipRRect(
          borderRadius: BorderRadius.circular(10),
          child: Stack(
            fit: StackFit.expand,
            children: <Widget>[
              Image.network(
                cardEntity.picUrl,
                fit: BoxFit.cover,
              ),
              Container(color: const Color(0x5a000000)),
              Container(
                margin: const EdgeInsets.all(20),
                alignment: Alignment.center,
                child: Text(
                  cardEntity.text,
                  textAlign: TextAlign.center,
                  style: const TextStyle(
                    letterSpacing: 2,
                    fontSize: 22,
                    color: Colors.white,
                    fontWeight: FontWeight.bold,
                  ),
                  maxLines: 4,
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}
